/****************************************************************
* @file   : eth_func.hpp
* @author : NingJian (freegodly@gmail.com)
* @date   : 05/26/21 09:37:20
* @brief   : 
****************************************************************/
#pragma once

#include <cstdint>
#include <cstring>
#include <memory>
#include <utility>
#include <memory>
#include <mutex>

#include "eth_types.h"

using namespace std;

namespace eth { 
namespace mcore {

hash256 hash256_from_bytes(const uint8_t bytes[32]);

hash256 hash256_from_string(const std::string& str);

std::string hash256_to_string(const hash256& h256);

std::string hash512_to_string(const hash512& h256);

std::string hash1024_to_string(const hash1024& h256);

std::string hash2048_to_string(const hash2048& h256);

int find_epoch_number(const hash256& seed) ;

const std::shared_ptr<eth_context> get_eth_context(int epoch_number, bool isfull = false);

eth_result search(const union hash512* const light_cache, 
        const size_t light_cache_num_items,
        const union hash1024* const full_dataset, 
        const size_t full_dataset_num_items,
        const hash256& header,
        const hash256& boundary,
        uint64_t start_nonce,
        size_t iterations);




hash2048 calculate_dataset_item_2048(const eth_context& context, uint32_t index);
hash1024 calculate_dataset_item_1024(const union hash512* const light_cache, const size_t light_cache_num_items, uint32_t index);
hash512 bitwise_xor(const hash512& x, const hash512& y) ;
void build_light_cache(hash512 cache[], int num_items, const hash256& seed) ;
hash256 calculate_epoch_seed(int epoch_number);

hash256 keccak256(const hash256& input) ;

uint64_t load_le(const uint8_t* data);

void keccakf1600(uint64_t state[25]);

void keccak(uint64_t* out, size_t bits, const uint8_t* data, size_t size);

int fromHex(char _i);

char toHex(int _i);

uint32_t fnv1(uint32_t u, uint32_t v);
hash512 fnv1(const hash512& u, const hash512& v);

int eth_calculate_light_cache_num_items(int epoch_number);

int find_largest_prime(int upper_bound);

int eth_calculate_dataset_num_items(int epoch_number);

eth_context * create_eth_context(int epoch_number,  bool isfull);

void destroy_eth_context(eth_context * context);

union hash256 keccak256(const uint8_t* data, size_t size);

union hash512 keccak512(const uint8_t* data, size_t size);

hash256 hash_kernel(const union hash512* const light_cache, 
                    const size_t light_cache_num_items,
                    const union hash1024* const full_dataset, 
                    const size_t full_dataset_num_items,
                    const hash512& seed);

hash512 hash_seed(const hash256& header_hash, uint64_t nonce);

hash256 hash_final(const hash512& seed, const hash256& mix_hash);

void  multi_threads_init_full_dataset(eth_context* const context);

}  
}